package main

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	rd "math/rand"
	"net"
	"net/url"
	"os"
	"strings"
	"time"
)

/*
使用 RSA 密钥的 TLS 服务器证书和签发证书的 CA 必须使用长度大于或等于 2048 位的密钥。TLS 不再信任所用 RSA 密钥长度小于 2048 位的证书。
TLS 服务器证书和签发证书的 CA 必须在签名算法中使用 SHA-2 系列中的哈希算法。TLS 不再信任 SHA-1 签名的证书。
TLS 服务器证书必须在证书的“使用者备用名称”扩展中显示服务器的 DNS 名称。证书的 CommonName 中的 DNS 名称不再受信任。
此外，所有在 2019 年 7 月 1 日后签发的 TLS 服务器证书（如证书的 NotBefore 字段中所示）必须遵循以下准则：

TLS 服务器证书必须包含一个内含 id-kp-serverAuth OID 的 ExtendedKeyUsage (EKU) 扩展。
TLS 服务器证书的有效期必须为 825 天或更短（如证书的 NotBefore 和 NotAfter 字段中所示）。
*/

type CertInformation struct {
	Country            []string
	Organization       []string
	OrganizationalUnit []string
	EmailAddress       []string
	Province           []string
	Locality           []string
	CommonName         string
	CrtName, KeyName   string
	IsCA               bool
	Names              []pkix.AttributeTypeAndValue
	DNSNames           []string
	EmailAddresses     []string
	IPAddresses        []net.IP
	URIs               []*url.URL
	Days               int
}

// subjectPublicKeyInfo reflects the ASN.1 object defined in the X.509 standard.
//
// This is defined in crypto/x509 as "publicKeyInfo".
type subjectPublicKeyInfo struct {
	Algorithm pkix.AlgorithmIdentifier
	PublicKey asn1.BitString
}

// CreateSubjectKeyIdentifier generates Subject Key Identifier (SKI) using SHA-256
// hash of the public key bytes according to RFC 7093 section 2.
// 1) The keyIdentifier is composed of the leftmost 160-bits
// of the SHA-256 hash of the value of the BIT STRING subjectPublicKey
// (excluding the tag, length, and number of unused bits).
func CreateSubjectKeyIdentifier(pub crypto.PublicKey) ([]byte, error) {
	keyBytes, err := x509.MarshalPKIXPublicKey(pub)
	if err != nil {
		return nil, err
	}
	var spki subjectPublicKeyInfo
	_, err = asn1.Unmarshal(keyBytes, &spki)
	if err != nil {
		return nil, err
	}

	//id := sha256.Sum256(spki.PublicKey.Bytes)
	id := sha1.Sum(spki.PublicKey.Bytes)
	//return id[:20], nil
	return id[:20], nil
}

func CreateEcCert(ca *x509.Certificate, cakey any, info CertInformation) error {

	cert := newCertificate(info)

	Key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		log.Fatalln("generate private key failed", err)
		return err
	}
	ski, err := CreateSubjectKeyIdentifier(&Key.PublicKey)
	if err != nil {
		log.Fatalln("create subject key failed", err)
		return err
	}
	cert.SubjectKeyId = ski
	if cert.IsCA {
		cert.AuthorityKeyId = cert.SubjectKeyId
	} else {
		cert.AuthorityKeyId = ca.SubjectKeyId
	}
	var buf []byte
	cert.SignatureAlgorithm = x509.ECDSAWithSHA256
	if ca == nil || cakey == nil {
		// 创建自签名证书
		buf, err = x509.CreateCertificate(rand.Reader, cert, cert, &Key.PublicKey, Key)
		log.Println("create root ca cert", cert.Subject)
	} else {
		// 使用根证书签名
		buf, err = x509.CreateCertificate(rand.Reader, cert, ca, &Key.PublicKey, cakey)
		log.Println("ca sign a cert", cert.Subject, cert.DNSNames, cert.IPAddresses)
	}

	if err != nil {
		log.Fatalln("sign cert failed", err)
		return err
	}
	err = write(info.CrtName, "CERTIFICATE", buf)
	if err != nil {
		log.Fatalln("write cert failed", err)
		return err
	}
	buf, err = x509.MarshalPKCS8PrivateKey(Key)
	return write(info.KeyName, "PRIVATE KEY", buf)
}

func CreateRsaCert(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) error {
	Crt := newCertificate(info)
	Key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return err
	}

	var buf []byte
	if RootCa == nil || RootKey == nil {
		// 创建自签名证书
		buf, err = x509.CreateCertificate(rand.Reader, Crt, Crt, &Key.PublicKey, Key)
		log.Println("create root ca cert", Crt.Subject)
	} else {
		// 使用根证书签名
		buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey)
		log.Println("sign cert with root cert", RootCa.Subject, Crt.Subject)
	}

	if err != nil {
		return err
	}

	err = write(info.CrtName, "CERTIFICATE", buf)
	if err != nil {
		return err
	}

	buf = x509.MarshalPKCS1PrivateKey(Key)
	return write(info.KeyName, "PRIVATE KEY", buf)
}

// 编码写入文件
func write(filename, Type string, p []byte) error {
	File, err := os.Create("assets/" + filename)
	defer File.Close()
	if err != nil {
		log.Fatalln("create file failed", err)
		return err
	}
	var b = &pem.Block{Bytes: p, Type: Type}
	return pem.Encode(File, b)
}

func appendFile(filename string, t string, crt []byte) error {
	f, err := os.OpenFile("assets/"+filename, os.O_RDWR|os.O_APPEND, 0666)
	defer f.Close()
	if err != nil {
		log.Fatalln("appendFile ca cert to file failed", err)
		return err
	}
	var b = &pem.Block{Bytes: crt, Type: t}
	return pem.Encode(f, b)
}

func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) {
	rootcertificate, err = ParseCrt(crtPath)
	if err != nil {
		log.Fatalln("parse cert failed", err)
		return
	}
	rootPrivateKey, err = ParseKey(keyPath)
	if err != nil {
		log.Fatalln("parse private key failed", err)
		return
	}
	return rootcertificate, rootPrivateKey, err
}

func ParseEc(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *ecdsa.PrivateKey, err error) {
	rootcertificate, err = ParseCrt(crtPath)
	if err != nil {
		log.Fatalln("parse cert failed", err)
		return
	}
	rootPrivateKey, err = ParseEcKey(keyPath)
	if err != nil {
		log.Fatalln("parse private key failed", err)
		return
	}
	return rootcertificate, rootPrivateKey, err
}

func ParseCrt(path string) (*x509.Certificate, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln("read cert file failed", path)
		return nil, err
	}
	p := &pem.Block{}
	p, buf = pem.Decode(buf)
	return x509.ParseCertificate(p.Bytes)
}

func ParseKey(path string) (*rsa.PrivateKey, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln("read private key file failed", path)
		return nil, err
	}
	p, buf := pem.Decode(buf)
	return x509.ParsePKCS1PrivateKey(p.Bytes)
}
func ParseEcKey(path string) (*ecdsa.PrivateKey, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln("read private key file failed", path)
		return nil, err
	}
	p, buf := pem.Decode(buf)
	return x509.ParseECPrivateKey(p.Bytes)
}

func newCertificate(info CertInformation) *x509.Certificate {
	now := time.Now()
	notAfter := now.AddDate(0, 0, info.Days)
	cert := &x509.Certificate{
		SerialNumber: big.NewInt(rd.Int63()),
		Subject: pkix.Name{
			Country:            info.Country,
			Organization:       info.Organization,
			OrganizationalUnit: info.OrganizationalUnit,
			Province:           info.Province,
			CommonName:         info.CommonName,
			Locality:           info.Locality,
			ExtraNames:         info.Names,
		},
		NotBefore:             now,                                                                        // 证书的开始时间
		NotAfter:              notAfter,                                                                   // 证书的结束时间
		BasicConstraintsValid: true,                                                                       // 基本的有效性约束
		IsCA:                  info.IsCA,                                                                  // 是否是根证书
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, // 证书用途
		KeyUsage:              x509.KeyUsageDigitalSignature,
		EmailAddresses:        info.EmailAddress,
		DNSNames:              info.DNSNames,
		IPAddresses:           info.IPAddresses,
	}
	if cert.IsCA {
		cert.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign
		cert.MaxPathLen = -1
		cert.ExtKeyUsage = nil
	}

	return cert
}

/*
*
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
*/
func parseCa() (*x509.Certificate, any) {
	cakey := `
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg+SMVaPpPB12y5B/z
e3SlbTD2SokHwXQhH+2OxjVLobahRANCAARdKrO3opGb5Ae6RIwYNbjo9v8J9Awe
4ALlpldTXGLD1hY+JMKOhLBQ/piP8UaH7fErUA29h2tWnreEp99Q83q6
-----END PRIVATE KEY-----
`
	cacert := `
-----BEGIN CERTIFICATE-----
MIICaTCCAg+gAwIBAgIIFJaroRBIehAwCgYIKoZIzj0EAwIwgYYxCzAJBgNVBAYT
AkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAwDgYDVQQHEwdCZWlqaW5nMRswGQYDVQQK
ExJ3d3cuc29uaWNvbS5jb20uY24xGzAZBgNVBAsTEnd3dy5zb25pY29tLmNvbS5j
bjEZMBcGA1UEAxMQU09OSUNPTSBFQ0MgUk9PVDAgFw0yNDA2MTIwNTM4MjlaGA8y
MDU0MDYwNTA1MzgyOVowgYYxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5n
MRAwDgYDVQQHEwdCZWlqaW5nMRswGQYDVQQKExJ3d3cuc29uaWNvbS5jb20uY24x
GzAZBgNVBAsTEnd3dy5zb25pY29tLmNvbS5jbjEZMBcGA1UEAxMQU09OSUNPTSBF
Q0MgUk9PVDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABF0qs7eikZvkB7pEjBg1
uOj2/wn0DB7gAuWmV1NcYsPWFj4kwo6EsFD+mI/xRoft8StQDb2Ha1aet4Sn31Dz
erqjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBQecrQh+wNPC5a/l1kcjeDk+CB2DDAfBgNVHSMEGDAWgBQecrQh+wNPC5a/l1kc
jeDk+CB2DDAKBggqhkjOPQQDAgNIADBFAiEAjVBCEm4Rg0MTYzg5JCKm7LddVPzQ
PlM4RVUE7ul8VmMCIB2On9V5VFTUOqihW43NfsAUn0QNrJNCw+i1Fmwp63fp
-----END CERTIFICATE-----
`
	block, _ := pem.Decode([]byte(cakey))

	//privateKey, err := x509.ParseECPrivateKey(block.Bytes)
	privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		log.Fatalln("parse ec private key failed", err)
	}

	block, _ = pem.Decode([]byte(cacert))
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		log.Fatalln("parse ec certificate failed", err)
	}

	return cert, privateKey
}

func doCreateCert(isCa bool, cn string, ip []string, dns []string, days int) {
	certinfo := CertInformation{

		CommonName:         cn,
		Organization:       []string{"www.sonicom.com.cn"},
		OrganizationalUnit: []string{"www.sonicom.com.cn"},
		Country:            []string{"CN"},
		Province:           []string{"Beijing"},
		Locality:           []string{"Beijing"},
		IsCA:               isCa,
		Days:               days,

		CrtName: cn + ".pem",
		KeyName: cn + ".key",
	}
	if len(dns) > 0 {
		certinfo.DNSNames = dns
	}
	if len(ip) > 0 {
		certinfo.IPAddresses = []net.IP{}
		for _, i := range ip {
			certinfo.IPAddresses = append(certinfo.IPAddresses, net.ParseIP(i))
		}
	}
	var crt *x509.Certificate = nil
	var pri any = nil
	if !isCa {
		crt, pri = parseCa()
	}
	err := CreateEcCert(crt, pri, certinfo)
	if !isCa {
		appendFile(certinfo.CrtName, "CERTIFICATE", crt.Raw)
	}
	if err != nil {
		log.Println("Create crt error,Error info:", err)
	}
}

type stringSet []string

func (s *stringSet) String() string {
	return strings.Join(*s, ",")
}
func (s *stringSet) Set(arg string) error {
	if len(arg) > 0 {
		if strings.Contains(arg, ",") {
			*s = strings.Split(arg, ",")
		} else {
			*s = []string{arg}
		}
	} else {
		*s = []string{}
	}
	return nil
}
func (s *stringSet) Get() interface{} {
	return nil
}

var (
	name      string = "FRICA"
	Version          = ""
	GoVersion        = ""
	BuildTime        = ""
	GitCommit        = ""
)

func showVersion() {
	fmt.Printf("%s\n", name)
	fmt.Printf("Version:\t%s\n", Version)
	fmt.Printf("GitCommit:\t%s\n", GitCommit)
	fmt.Printf("GoVersion:\t%s\n", GoVersion)
	fmt.Printf("BuildTime:\t%s\n", BuildTime)
}
func main() {
	ver := false
	flag.BoolVar(&ver, "v", false, "show version info")
	var cn string
	var ip stringSet
	var dns stringSet
	var days int
	var isCa bool
	flag.StringVar(&cn, "cn", "", "CommonName")
	flag.Var(&ip, "ip", "逗号分隔的IP地址")
	flag.Var(&dns, "dns", "逗号分隔的DNS")
	flag.IntVar(&days, "days", 825, "days")
	flag.BoolVar(&isCa, "ca", false, "是否创建根CA")
	//ip = []string{}
	//for i := 1; i < 255; i++ {
	//	ip = append(ip, "192.168.9."+strconv.Itoa(i))
	//}
	//for i := 1; i < 255; i++ {
	//	ip = append(ip, "192.168.10."+strconv.Itoa(i))
	//}
	//for i := 1; i < 255; i++ {
	//	ip = append(ip, "192.168.11."+strconv.Itoa(i))
	//}
	flag.Parse()
	if ver {
		showVersion()
		return
	}
	if len(ip) > 0 && len(cn) == 0 {
		cn = ip[0]
	}
	if len(cn) == 0 {
		log.Fatalln("invalid cn")
		return
	}
	log.Println("cn:", cn, " ip: ", ip, " dns: ", dns, " days: ", days, " ca: ", isCa)
	doCreateCert(isCa, cn, ip, dns, days)
}
